package com.pandy.zerocopy;

import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

/**
 * @description:Java实现文件拷贝的几种方式
 * @author: panhong
 * @date: 2025/4/22
 */
@Slf4j
public class CopyFileMain {

    public static void main(String[] args) {
        String inputFilePath = "D:\\BaiduNetdiskDownload\\copyFile.zip";
        String outputFilePath = "D:\\BaiduNetdiskDownload\\copyFile-1.zip";


        // 普通的Java IO流
        inputStreamCopyFile(inputFilePath,outputFilePath);

        // 普通的Java带Buffer的IO流
         // bufferedInputStreamCopyFile(inputFilePath,outputFilePath);

        // 零拷贝实现之mmap的IO流
        //mmapCopyFile(inputFilePath,outputFilePath);

        // 零拷贝实现之sendfile的IO流
        //sendfileCopyFile(inputFilePath,outputFilePath);
    }


    /**
     * 零拷贝实现之sendfile的IO流 完成文件复制
     * @param inputFilePath
     * @param outputFilePath
     */
    private static void sendfileCopyFile(String inputFilePath, String outputFilePath) {
        final long start = System.currentTimeMillis();
        try(
                final FileChannel inputChannel = new FileInputStream(inputFilePath).getChannel();
                final FileChannel outputChannel = new FileOutputStream(outputFilePath).getChannel();
        ) {
            // 方式1：针对小于2G的，最大拷贝为2G，超过2G部分将丢弃
            // inputChannel.transferTo(0,inputChannel.size(), outputChannel);

            // 方式1：针对大于2G的
            final long size = inputChannel.size();
            for(long left = size; left > 0;) {
                final long transferSize = inputChannel.transferTo((size - left), left, outputChannel);

                // 剩下还有多少
                left = left - transferSize;

                log.info("文件总大小：{}，拷贝大小：{}，剩下大小：{}", size, transferSize, left);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        final long end = System.currentTimeMillis();
        log.info("【sendfileCopyFile】，总耗时：{}", (end - start));
    }


    /**
     * 零拷贝实现之mmap的IO流 完成文件复制
     * @param inputFilePath
     * @param outputFilePath
     */
    private static void mmapCopyFile(String inputFilePath, String outputFilePath) {
        final long start = System.currentTimeMillis();
        try(
                final FileChannel inputChannel = new FileInputStream(inputFilePath).getChannel();
                final FileChannel outputChannel = new RandomAccessFile(outputFilePath,"rw").getChannel();
        ) {
            long size = inputChannel.size();
            log.info("【mmapCopyFile】 fileSize: {}", size);
            final MappedByteBuffer mapIn = inputChannel.map(FileChannel.MapMode.READ_ONLY, 0, size);
            final MappedByteBuffer mapOut = outputChannel.map(FileChannel.MapMode.READ_WRITE, 0, size);

            for(int i=0; i<size; i++) {
                final byte b = mapIn.get(i);
                mapOut.put(b);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        final long end = System.currentTimeMillis();
        log.info("【mmapCopyFile】，总耗时：{}", (end - start));
    }

    /**
     * 普通的Java带Buffer的IO流 完成文件复制
     * @param inputFilePath
     * @param outputFilePath
     */
    private static void bufferedInputStreamCopyFile(String inputFilePath, String outputFilePath) {
        final long start = System.currentTimeMillis();
        try(
                final BufferedInputStream bis = new BufferedInputStream(new FileInputStream(inputFilePath));
                final BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(outputFilePath));
        ) {
            byte[] b = new byte[64];
            int len;
            while ((len = bis.read(b)) != -1) {
                bos.write(b);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        final long end = System.currentTimeMillis();
        log.info("【bufferedInputStreamCopyFile】，总耗时：{}", (end - start));
    }

    /**
     * 普通的Java IO流 完成文件复制
     * @param inputFilePath
     * @param outputFilePath
     */
    private static void inputStreamCopyFile(String inputFilePath, String outputFilePath) {
        final long start = System.currentTimeMillis();
        try(
                FileInputStream fis = new FileInputStream(inputFilePath);
                final FileOutputStream fos = new FileOutputStream(outputFilePath);
        ) {
            byte[] b = new byte[64];
            int len;
            while ((len = fis.read(b)) != -1) {
                fos.write(b);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        final long end = System.currentTimeMillis();
        log.info("【inputStreamCopyFile】，总耗时：{}", (end - start));
    }
}
